/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package util

import java.lang.ProcessBuilder
import java.lang.Process
import java.io.InputStream
import java.io.PrintStream
import java.io.IOException
import java.net.URLClassLoader
import scala.concurrent.ops
import scala.collection.JavaConversions._

/**
 * Module to invoke java application.
 */
object JavaInvoker {
  class Exception(msg: String) extends java.lang.Exception(msg)

  lazy val classPath = {
    val forScala:List[String] = this.getClass().getClassLoader() match {
      case l:URLClassLoader => l.getURLs.map(_.getPath).toList
      case _                => List()
    }
    val forApp: List[String] = classOf[scala.collection.immutable.List[Int]].getClassLoader() match {
      case l:URLClassLoader => l.getURLs.map(_.getPath).toList
      case _ => List()
    }
    forScala ++ forApp match {
      case Nil => System.getProperty("java.class.path")
      case x   => x.mkString(System.getProperty("path.separator"))
    }
  }

  lazy val javaCommand = {
    val h = System.getenv("JAVA_HOME")
    if (h == null) {
      throw new Exception("JAVA_HOME has benn not set.")
    }
    h + "/bin/java"
  }

  /**
   * Invokes Java application.
   *
   * @param className Entry point class.
   * @param args      comannd line arguments.
   * @param props     System properties of the process which will be invoked.
   * 
   * @return The process object which is invoked.
   */
  def invoke(className: String, args: List[String], props: Map[String, String]): Process = {
    require(classPath != null)
    require(className != null)

    val propOpts = props.map{ case (k, v) => "-D%s=%s".format(k, v) }
    val cmd = List(javaCommand, "-classpath", classPath) ++ propOpts ++ List(className) ++ args

    val p = new ProcessBuilder(cmd).start

    def pipe(is: InputStream, ps: PrintStream) {
      try {
        while (
          is.read() match {
            case -1 => false
            case x  => ps.write(x);true
          }
        ) {}
      } catch {
        case e: IOException => ps.println(e)
      }
    }

    ops.spawn(pipe(p.getInputStream(), System.out))
    ops.spawn(pipe(p.getErrorStream(), System.err))

    return p
  }
}
